﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel.Dispatcher;
using Sripirom.ATS.Core.ExceptionHandler;
using Sripirom.ATS.Core.Logging;
using Sripirom.ATS.Core.Validator;

namespace Sripirom.ATS.Core.ServiceModel.Dispatcher
{
    /// <summary>
    /// Validates incoming parameters using Data Annotations
    /// the service operation call
    /// </summary>
    public class ValidatingParameterInspector : IParameterInspector
    {
        private readonly IEnumerable<IObjectValidator> _validators;
        private readonly IErrorMessageGenerator _errorMessageGenerator;

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidatingParameterInspector"/> class.
        /// </summary>
        /// <param name="validatiors">The validators.</param>
        /// <param name="errorMessageGenerator">The error message generator.</param>
        public ValidatingParameterInspector(IEnumerable<IObjectValidator> validatiors, IErrorMessageGenerator errorMessageGenerator)
        {
            LoggerFactory.CreateLog().Debug("I:Constructor");

            if (validatiors == null)
            {
                LoggerFactory.CreateLog().Debug("Error:validatiors == null");
                throw new ArgumentOutOfRangeException("validators");
            }
            if (!validatiors.Any())
            {
                LoggerFactory.CreateLog().Debug("Error:!validatiors.Any()"); 
                throw new ArgumentException("At least one validator is required.");
            }
            if (errorMessageGenerator == null)
            {
                LoggerFactory.CreateLog().Debug("Error:errorMessageGenerator == null"); 
                throw new ArgumentNullException("errorMessageGenerator");
            }
            _validators = validatiors;
            _errorMessageGenerator = errorMessageGenerator;

            LoggerFactory.CreateLog().Debug("O:Constructor");
        }


        protected void Innit() 
        {
   
            MessageException = null;
        }

        public MessageException MessageException { get; set; }

        /// <summary>
        /// Called before client calls are sent and after service responses are returned.
        /// </summary>
        /// <param name="operationName">The name of the operations.</param>
        /// <param name="inputs">The objects passed to the method by the client.</param>
        /// <returns>
        /// The correlation state that is returned as the <paramref name="correlationState"/>
        /// paramter in <see cref="M:System.ServiceModel.Dispatcher.IParameterInspector.AfterCall(System.String,System.Object[],System.Object,System.Object)"/>.
        /// Return null if you do not intend to use correlation state.
        /// </returns>
        /// <exception cref="System.ServiceModel.FaultException"></exception>
        public object BeforeCall(string operationName, object[] inputs)
        {
            Innit();
            Stopwatch sw = new Stopwatch();
            if (inputs == null)
            {
                MessageException = new MessageException(Errors.UNKNOWN_ERROR);
                return null;
            }
            if (!inputs.Any())
            {
                MessageException = new MessageException(Errors.UNKNOWN_ERROR);
                return null;
            }

            //TChannelRequest request = (TChannelRequest)inputs[0];
            LoggerFactory.CreateLog().Debug(String.Format("I:BeforeCall {0}.", operationName));
            try
            {
                sw.Start();
                var validationResults = new List<ValidationResult>();

                foreach (var input in inputs)
                {
                    foreach (var validator in _validators)
                    {
                        var results = validator.Validate(input);
                        validationResults.AddRange(results);
                    }
                }
                sw.Stop();

            }
            catch (MessageException ex)
            {
                MessageException = ex;
                return ex;
            }
            catch (Exception ex)
            {
                MessageException = new MessageException(Errors.INVALID_REQUEST_MESSAGE, ex);
                return MessageException;
            }
            finally {
                if (sw.IsRunning)
                    sw.Stop();

                LoggerFactory.CreateLog().Debug(String.Format("O:BeforeCall {0}. Time of travel: {1}", operationName, sw.Elapsed));
            }
        
          

            return null;
        }

        /// <summary>
        /// Called after client calls are returned and before service response are sent.
        /// </summary>
        /// <param name="operationName">The name of the invoked operation.</param>
        /// <param name="outputs">Any output objects.</param>
        /// <param name="returnValue">The return value of the operation.</param>
        /// <param name="correlationState">Any correlation state returned from the 
        /// <see cref="M:System.ServiceModel.Dispatcher.IParameterInspector.BeforeCall(System.String,System.Object[])" /> method,
        /// or null.
        /// </param>
        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
            LoggerFactory.CreateLog().Debug(String.Format("I:AfterCall {0}.", operationName));
            //if (correlationState != null)
            //{
            //    returnValue = correlationState;
            //}

            LoggerFactory.CreateLog().Debug(String.Format("O:AfterCall {0}.", operationName));
        }


        //protected TChannelReply CreatResponse(TChannelRequest request)
        //{
        //    TChannelReply response = new TChannelReply();
        //    var headerRequest = GetPropValue(request, "HEADER");
        //    var headerResponse = GetPropValue(response, "HEADER");

        //    var properties = TypeDescriptor.GetProperties(headerResponse.GetType())
        //                                     .Cast<PropertyDescriptor>()
        //                                     .Where(p => !p.IsReadOnly);
        //    foreach (var property in properties)
        //    {
        //        SetPropValue(headerResponse, property.Name, GetPropValue(headerRequest, property.Name));
        //    }

        //    return response;
        //} 
        //public void SetPropValue(object src, string propName, object value)
        //{
        //    src.GetType().GetProperty(propName).SetValue(src, value, null);
        //}
        //public object GetPropValue(object src, string propName)
        //{
        //    return src.GetType().GetProperty(propName).GetValue(src, null);
        //}
    }
}